/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.loaders; import java.lang.reflect.*; import java.io.*; import org.openide.TopManager; import org.openide.cookies.InstanceCookie; import org.openide.filesystems.FileObject; import org.openide.util.HelpCtx; // imports for findHelp: import java.beans.*; import javax.swing.JComponent; import org.openide.windows.TopComponent; import org.openide.nodes.Node; import org.openide.ServiceType; import org.openide.loaders.DataObject; import org.openide.util.datatransfer.NewType; import org.openide.util.datatransfer.PasteType; import org.openide.DialogDescriptor; import org.openide.WizardDescriptor; import org.openide.util.actions.SystemAction; import org.openide.modules.ManifestSection; import org.openide.options.SystemOption; import org.openide.util.Utilities; import org.openide.util.SharedClassObject; // Encapsulates working with classes and optimize it. /** An instance cookie implementation that works with files or entries. * * @author Jan Jancura, Jaroslav Tulach */ public class InstanceSupport extends Object implements InstanceCookie { /** entry to work with */ private MultiDataObject.Entry entry; /** throw exception during loading of the class */ private Throwable clazzException; /** the class of the instance */ private Class clazz; /** the class is applet */ private Boolean applet; /** the class is bean */ private Boolean bean; /** New support for given entry. The file is taken from the * entry and is updated if the entry moves or renames itself. * @param entry entry to create instance from */ public InstanceSupport(MultiDataObject.Entry entry) { this.entry = entry; } // main methods ......................................................................................................... /* The bean name for the instance. * @return the name for the instance */ public String instanceName () { return instanceOrigin ().getPackageName ('.'); } /* The class of the instance represented by this cookie. * Can be used to test whether the instance is of valid * class before it is created. * * @return the class of the instance * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Class instanceClass () throws java.io.IOException, ClassNotFoundException { if (clazzException != null) { if (clazzException instanceof IOException) throw (IOException)clazzException; else if (clazzException instanceof ClassNotFoundException) throw (ClassNotFoundException)clazzException; else if (clazzException instanceof RuntimeException) throw (RuntimeException)clazzException; else throw (ThreadDeath)clazzException; } if (clazz != null) return clazz; //System.out.println ("getClass " + fileName ); // NOI18N try { if (instanceOrigin ().getExt ().equals ("ser")) { // NOI18N // read class from ser file InputStream is = instanceOrigin ().getInputStream (); try { clazz = readClass (is); return clazz; } finally { is.close (); } } else { // find class by class loader clazz = findClass (instanceName ()); if (clazz == null) throw new ClassNotFoundException (); return clazz; } } catch (IOException ex) { clazzException = ex; throw ex; } catch (ClassNotFoundException ex) { clazzException = ex; throw ex; } catch (RuntimeException ex) { clazzException = ex; throw ex; } catch (ThreadDeath t) { clazzException = t; throw t; } catch (Throwable t) { // turn other throwables into class not found ex. throw (ClassNotFoundException) (clazzException = new ClassNotFoundException (t.getMessage ())); } } /** Returns the origin of the instance. * @see InstanceCookie.Origin#instanceOrigin * @return the origin */ public FileObject instanceOrigin () { // return getEntry ().getFile (); return entry.getFile (); } /* * @return an object to work with * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Object instanceCreate () throws java.io.IOException, ClassNotFoundException { try { if (instanceOrigin ().getExt ().equals ("ser")) { // NOI18N // create from ser file Object o = java.beans.Beans.instantiate ( TopManager.getDefault ().currentClassLoader (), instanceName () ); return o; } else { Class c = instanceClass (); if (SharedClassObject.class.isAssignableFrom (c)) { // special support return SharedClassObject.findObject (c, true); } else { // create new instance return c.newInstance (); } } } catch (IOException ex) { throw ex; } catch (ClassNotFoundException ex) { throw ex; } catch (RuntimeException ex) { throw ex; } catch (ThreadDeath t) { throw t; } catch (Throwable t) { // turn other throwables into class not found ex. throw (ClassNotFoundException) (clazzException = new ClassNotFoundException (t.getMessage ())); } } /** Is this an applet? * @return <code>true</code> if this class is an {@link Applet} */ public boolean isApplet () { if (applet != null) return applet.booleanValue (); boolean b = false; try { b = java.applet.Applet.class.isAssignableFrom (instanceClass ()); } catch (RuntimeException ex) { throw ex; } catch (ThreadDeath t) { throw t; } catch (Throwable t) { // false when other errors occur (NoClassDefFoundError etc...) applet = Boolean.FALSE; return false; } applet = new Boolean (b); return b; } /** Is this a standalone executable? * @return <code>true</code> if this class has main method * (e.g., <code>public static void main (String[] arguments)</code>). */ public boolean isExecutable () { try { Method main = instanceClass ().getDeclaredMethod ("main", new Class[] { // NOI18N String[].class }); int m = main.getModifiers (); return Modifier.isPublic (m) && Modifier.isStatic (m) && Void.TYPE.equals ( main.getReturnType () ); } catch (RuntimeException ex) { throw ex; } catch (ThreadDeath t) { throw t; } catch (Throwable t) { // false when other errors occur (NoClassDefFoundError etc...) // [PENDING] ThreadDeath (ditto for other methods) return false; } } /** Is this a JavaBean? * @return <code>true</code> if this class represents JavaBean (is public and has a public default constructor). */ public boolean isJavaBean () { if (bean != null) return bean.booleanValue (); // try to find out... try { int modif = instanceClass ().getModifiers (); if (!Modifier.isPublic (modif) || Modifier.isAbstract (modif)) { bean = Boolean.FALSE; return false; } Constructor c; try { c = instanceClass ().getConstructor (new Class [0]); } catch (NoSuchMethodException e) { bean = Boolean.FALSE; return false; } if ((c == null) || !Modifier.isPublic (c.getModifiers ())) { bean = Boolean.FALSE; return false; } } catch (RuntimeException ex) { throw ex; } catch (ThreadDeath t) { throw t; } catch (Throwable t) { // false when other errors occur (NoClassDefFoundError etc...) bean = Boolean.FALSE; return false; } // okay, this is bean... // return isBean = java.io.Serializable.class.isAssignableFrom (clazz); bean = Boolean.TRUE; return true; } /** Is this an interface? * @return <code>true</code> if the class is an interface */ public boolean isInterface () { try { return instanceClass ().isInterface (); } catch (RuntimeException ex) { throw ex; } catch (ThreadDeath t) { throw t; } catch (Throwable t) { // false when other errors occur (NoClassDefFoundError etc...) return false; } } public String toString () { return instanceName (); } /** Find context help for some instance. * Helper method useful in nodes or data objects that provide an instance cookie; * they may choose to supply their own help context based on this. * All API classes which can provide help contexts will be tested for * (including <code>HelpCtx</code> instances themselves). * <code>JComponent</code>s are checked for an attached help ID property, * as with {@link HelpCtx#findHelp} (but not traversing parents). * <p>Also, partial compliance with the JavaHelp section on JavaBeans help is implemented--i.e., * if a Bean in its <code>BeanInfo</code> provides a <code>BeanDescriptor</code> which * has the attribute <code>helpID</code>, this will be returned. The value is not * defaulted (because it would usually be nonsense and would mask a useful default * help for the instance container), nor is the help set specification checked, * since someone should have installed the proper help set anyway, and the APIs * cannot add a new reference to a help set automatically. * See <code>javax.help.HelpUtilities.getIDStringFromBean</code> for details. * <p>Special IDs are added, corresponding to the class name, for all standard visual components. * @param instance the instance to check for help (it is permissible for the {@link InstanceCookie#instanceCreate} to return <code>null</code>) * @return the help context found on the instance or inferred from a Bean, * or <code>null</code> if none was found (or it was {@link HelpCtx#DEFAULT_HELP}) */ public static HelpCtx findHelp (InstanceCookie instance) { Class clazz = null; try { clazz = instance.instanceClass (); // First try known API classes. if (TopComponent.class.isAssignableFrom (clazz) || Node.class.isAssignableFrom (clazz) || DataObject.class.isAssignableFrom (clazz) || NewType.class.isAssignableFrom (clazz) || PasteType.class.isAssignableFrom (clazz) || DialogDescriptor.class.isAssignableFrom (clazz) || WizardDescriptor.Panel.class.isAssignableFrom (clazz) || SystemAction.class.isAssignableFrom (clazz) || ManifestSection.FileSystemSection.class.isAssignableFrom (clazz) || SystemOption.class.isAssignableFrom (clazz) || ServiceType.class.isAssignableFrom (clazz) || HelpCtx.class.isAssignableFrom (clazz)) { HelpCtx test; Object obj = instance.instanceCreate (); if (obj instanceof TopComponent) test = ((TopComponent) obj).getHelpCtx (); else if (obj instanceof Node) test = ((Node) obj).getHelpCtx (); else if (obj instanceof DataObject) test = ((DataObject) obj).getHelpCtx (); else if (obj instanceof NewType) test = ((NewType) obj).getHelpCtx (); else if (obj instanceof PasteType) test = ((PasteType) obj).getHelpCtx (); else if (obj instanceof DialogDescriptor) test = ((DialogDescriptor) obj).getHelpCtx (); else if (obj instanceof WizardDescriptor.Panel) test = ((WizardDescriptor.Panel) obj).getHelp (); else if (obj instanceof SystemAction) test = ((SystemAction) obj).getHelpCtx (); else if (obj instanceof ManifestSection.FileSystemSection) test = ((ManifestSection.FileSystemSection) obj).getHelpCtx (); else if (obj instanceof SystemOption) test = ((SystemOption) obj).getHelpCtx (); else if (obj instanceof ServiceType) test = ((ServiceType) obj).getHelpCtx (); else if (obj instanceof HelpCtx) test = (HelpCtx) obj; else test = null; // obj==null or bad cookie if (test != null && ! test.equals (HelpCtx.DEFAULT_HELP)) return test; } // If a component, look for attached help. if (JComponent.class.isAssignableFrom (clazz)) { JComponent comp = (JComponent) instance.instanceCreate (); if (comp != null) { String hid = (String) comp.getClientProperty ("HelpID"); // NOI18N if (hid != null) return new HelpCtx (hid); } } // Look for Bean help. Also works on components not found above. BeanDescriptor desc = Utilities.getBeanInfo (clazz).getBeanDescriptor (); if (desc != null) { // [PENDING] ideally would also look for a help set and add that to the system // set if found, but there is no API for this at the moment String val = (String) desc.getValue ("helpID"); // NOI18N if (val != null) return new HelpCtx (val); } // Help on some standard components. Note that borders/layout managers do not really work here. if (java.awt.Component.class.isAssignableFrom (clazz) || java.awt.MenuComponent.class.isAssignableFrom (clazz)) { String name = clazz.getName (); String[] pkgs = new String[] { "java.awt.", "javax.swing.", "javax.swing.border." }; // NOI18N for (int i = 0; i < pkgs.length; i++) { if (name.startsWith (pkgs[i]) && name.substring (pkgs[i].length ()).indexOf ('.') == -1) return new HelpCtx (name); } } // All failed. return null; } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) { // NOI18N System.err.println ("During inspection of " + instance + " (class " + clazz + "):"); e.printStackTrace (); } return null; } } /** Reads a class from input stream. Expects a serialized object to be stored * in the stream and reads only a class from it. * @param is input stream to read from * @return the class of that stream * @exception IOException if something fails */ private Class readClass (InputStream is) throws IOException, ClassNotFoundException { /** object input stream */ class OIS extends ObjectInputStream { public OIS (InputStream iss) throws IOException { super (iss); } /** Throws exception to signal the kind of class found. */ public Class resolveClass (ObjectStreamClass osc) throws IOException, ClassNotFoundException { throw new ClassEx (findClass (osc.getName ())); } }; ObjectInputStream ois = new OIS (new BufferedInputStream (is)); try { ois.readObject (); // should not happen throw new ClassNotFoundException (); } catch (ClassEx ex) { // good, we found the class return ex.clazz; } } /** Finds a class for given name. * @param name name of the class * @return the class for the name * @exception ClassNotFoundException if the class cannot be found */ private Class findClass (String name) throws ClassNotFoundException { try { return TopManager.getDefault ().currentClassLoader ().loadClass (name); } catch (ClassNotFoundException ex) { throw ex; } catch (RuntimeException ex) { throw ex; } catch (ThreadDeath t) { throw t; } catch (Throwable t) { // turn other throwables into class not found ex. throw new ClassNotFoundException (t.getMessage ()); } } /** Enhanced instance cookie support that also knows the file it * has been created from and can be serialized back to. * Note that <code>InstanceSupport</code> already does; this class * only declares the interface. */ public static class Origin extends InstanceSupport implements InstanceCookie.Origin { /** New support for a given entry. The file is taken from the * entry and is updated if the entry moves or renames itself. * @param entry entry to create instance from */ public Origin (MultiDataObject.Entry entry) { super (entry); } } /** Trivial supporting instance cookie for already-existing objects. */ public static class Instance extends Object implements InstanceCookie { /** the object to represent */ private Object obj; /** Create a new instance cookie. * @param obj the object to represent in this cookie */ public Instance (Object obj) { this.obj = obj; } /* The bean name for the instance. * @return the name for the instance */ public String instanceName () { return obj.getClass ().getName (); } /* The class of the instance represented by this cookie. * Can be used to test whether the instance is of valid * class before it is created. * * @return the class of the instance */ public Class instanceClass () { return obj.getClass (); } /* * @return an object to work with */ public Object instanceCreate () { return obj; } } /** The exception to use to signal succesful find of a class. * Used in method readClass. */ private class ClassEx extends IOException { /** founded class */ public Class clazz; static final long serialVersionUID =4810039297880922426L; /** @param c the class */ public ClassEx (Class c) { clazz = c; } } } /* * Log * 23 Gandalf 1.22 1/13/00 Ian Formanek NOI18N * 22 Gandalf 1.21 1/12/00 Ian Formanek NOI18N * 21 Gandalf 1.20 12/29/99 Jaroslav Tulach Special handling for * SharedClassObject. * 20 Gandalf 1.19 11/5/99 Jesse Glick Help context on visual * component classes. * 19 Gandalf 1.18 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 18 Gandalf 1.17 10/5/99 Jesse Glick Only conditionally * reporting exceptions in findHelp. * 17 Gandalf 1.16 9/10/99 Jaroslav Tulach Changes in services * APIs. * 16 Gandalf 1.15 8/9/99 Ian Formanek Generated Serial Version * UID * 15 Gandalf 1.14 7/21/99 Jesse Glick Bugfix (TopComponent's * in Component Palette). * 14 Gandalf 1.13 7/20/99 Jesse Glick findHelp bugfixes. * 13 Gandalf 1.12 7/11/99 David Simonek window system change... * 12 Gandalf 1.11 7/7/99 Jesse Glick System options & * executor types have help contexts. * 11 Gandalf 1.10 6/25/99 Jesse Glick Instances can have * sensible help contexts. * 10 Gandalf 1.9 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 9 Gandalf 1.8 4/16/99 Libor Martinek * 8 Gandalf 1.7 3/26/99 Jaroslav Tulach * 7 Gandalf 1.6 3/14/99 Jaroslav Tulach Change of * MultiDataObject.Entry. * 6 Gandalf 1.5 3/10/99 Jesse Glick [JavaDoc] * 5 Gandalf 1.4 1/20/99 David Simonek * 4 Gandalf 1.3 1/20/99 David Simonek rework of class DO * 3 Gandalf 1.2 1/17/99 Jaroslav Tulach * 2 Gandalf 1.1 1/6/99 Ian Formanek * 1 Gandalf 1.0 1/5/99 Ian Formanek * $ */